package cn.initcap.algorithm.swing.fractal.tree;

import cn.initcap.algorithm.swing.AlgoVisHelper;
import cn.initcap.algorithm.swing.BaseAlgoFrame;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;

/**
 * @author initcap
 * @date 8/1/19 2:45 PM
 */
public class AlgoVisualizer {

    private static int DELAY = 40;
    private FractalData data;
    private BaseAlgoFrame frame;

    public AlgoVisualizer(int sceneWidth, int sceneHeight, int maxDepth, double splitAngle) {

        data = new FractalData(maxDepth, splitAngle);

        EventQueue.invokeLater(() -> {
            frame = new BaseAlgoFrame("Fractal Visualizer", sceneWidth, sceneHeight) {
                @Override
                public void draw(Graphics graphics) {
                    drawFractal((Graphics2D) graphics, sceneWidth / 2, sceneHeight, sceneHeight, 0, 0);
                }
            };
            frame.addKeyListener(new AlgoKeyListener());
            new Thread(() -> {
                run();
            }).start();
        });
    }

    public static void main(String[] args) {

        int maxDepth = 6;
        double splitAngle = 60.0;
        int sceneWidth = 800;
        int sceneHeight = 800;

        new AlgoVisualizer(sceneWidth, sceneHeight, maxDepth, splitAngle);
    }

    private void run() {

        setData(data.depth);
    }

    private void setData(int depth) {
        data.depth = depth;

        frame.render(data);
        AlgoVisHelper.pause(DELAY);
    }

    public void addAlgoKeyListener() {
        frame.addKeyListener(new AlgoKeyListener());
    }

    private void drawFractal(Graphics2D g, double x1, double y1, double side, double angle, int depth) {

        double side2 = side / 2;

        if (side2 <= 0) {
            return;
        }

        if (depth == data.depth) {
            double x2 = x1 - side * Math.sin(angle * Math.PI / 180.0);
            double y2 = y1 - side * Math.cos(angle * Math.PI / 180.0);
            AlgoVisHelper.setColor(g, AlgoVisHelper.INDIGO);
            AlgoVisHelper.drawLine(g, x1, y1, x2, y2);
            return;
        }

        double x2 = x1 - side2 * Math.sin(angle * Math.PI / 180.0);
        double y2 = y1 - side2 * Math.cos(angle * Math.PI / 180.0);
        AlgoVisHelper.setColor(g, AlgoVisHelper.INDIGO);
        AlgoVisHelper.drawLine(g, x1, y1, x2, y2);

        drawFractal(g, x2, y2, side / 2, angle + data.splitAngle / 2, depth + 1);
        drawFractal(g, x2, y2, side / 2, angle - data.splitAngle / 2, depth + 1);

    }

    private class AlgoKeyListener extends KeyAdapter {

        @Override
        public void keyReleased(KeyEvent event) {
            if (event.getKeyChar() >= '0' && event.getKeyChar() <= '9') {
                int depth = event.getKeyChar() - '0';
                setData(depth);
            }
        }
    }
}
